"""
Modules of elements with Fourier expansion and partially known relations. 

AUTHOR :
    -- Martin Raum (2009 - 10 - 30) Initial version
"""

#===============================================================================
# 
# Copyright (C) 2009 Martin Raum
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
#===============================================================================

from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_ambient import GradedExpansionAmbient_abstract
from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_element import GradedExpansionVector_class
from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_ring import GradedExpansionRing_class
from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_submodule import GradedExpansionSubmodule_abstract
from sage.misc.flatten import flatten
from sage.misc.latex import latex
from sage.modules.module import Module
from sage.rings.all import Integer
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.structure.element import Element
import operator

#===============================================================================
# GradedExpansionModule_class
#===============================================================================

class GradedExpansionModule_class ( GradedExpansionAmbient_abstract, Module ) :
    """
    A class for a module of vector valued graded expansions
    over an ambient of graded expansions, that might also be trivial.
    
    SEE:
        :class:~`fourier_expansion_framework.gradedexpansions.gradedexpansion_ring.GradedExpansionRing_class`.
    """
    
    def __init__ ( self, base_ring_generators, generators, relations,
                   grading, all_relations = True, reduce_before_evaluating = True ) :
        """
        The degree one part of the monomials that correspond to generators over the
        base expansion ring will serve as the coordinates of the elements.
        
        INPUT:
            - ``base_ring_generators``      -- A list of (equivariant) monoid power series with
                                               coefficient domain the base ring of the coefficient
                                               domain of the generators or ``None``.
            - ``generators``                -- A list of (equivariant) monoid power series; The generators
                                               of the ambient over the ring generated by the base ring
                                               generators.
            - ``relations``                 -- An ideal in a polynomial ring with ``len(base_ring_generators) + len(generators)``
                                               variables.
            - ``grading``                   -- A grading deriving from :class:~`fourier_expansion_framework.gradedexpansions.gradedexpansion_grading`;
                                               A grading for the polynomial ring of the relations.
            - ``all_relations``             -- A boolean (default: ``True``); If ``True`` the relations given
                                               for the polynomial ring are all relations that the Fourier
                                               expansion have.
            - ``reduce_before_evaluating``  -- A boolean (default: ``True``); If ``True`` any monomial
                                               will be Groebner reduced before the Fourier expansion
                                               is calculated.

        NOTE:
            The grading must respect the relations of the generators.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import *
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_module import *
            sage: m = FreeModule(QQ, 3)
            sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False))
            sage: mps = mpsm.base_ring()
            sage: ger = GradedExpansionModule_class(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mpsm, {1: m([1,1,1]), 2: m([1,3,-3])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2)))
            sage: ger.base_ring()
            Graded expansion ring with generators a
        """
        if not hasattr(self, '_element_class') :
            self._element_class = GradedExpansionVector_class
        
        if hasattr(self, "_extended_base_ring") :
            Module.__init__(self, self._extended_base_ring)
        elif base_ring_generators is None or len(base_ring_generators) == 0 :
            Module.__init__(self, relations.base_ring())
        else :
            gb = filter( lambda p: all( all(a == 0 for a in list(e)[len(base_ring_generators):])
                                        for e in p.exponents() ),
                         relations.groebner_basis() )
            P = PolynomialRing( relations.base_ring(),
                                list(relations.ring().variable_names())[:len(base_ring_generators)] )
            base_relations = P.ideal(gb)
            R = GradedExpansionRing_class(None, base_ring_generators, base_relations,
                    grading.subgrading(xrange(len(base_ring_generators))), all_relations, reduce_before_evaluating)
            Module.__init__(self, R)

        GradedExpansionAmbient_abstract.__init__(self, base_ring_generators, generators, relations, grading, all_relations, reduce_before_evaluating)
        
        self._populate_coercion_lists_(
          convert_list = [self.relations().ring()],
          convert_method_name = "_graded_expansion_submodule_to_graded_ambient_" )

    def _graded_monoms(self, index) :
        """
        Return all monoms in the generators that have a given grading index.
        
        INPUT:
            - ``index`` -- A grading values.
        
        OUTPUT:
            A list of elements of ``self``.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import *
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_module import *
            sage: m = FreeModule(QQ, 3)
            sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False))
            sage: mps = mpsm.base_ring()
            sage: ger = GradedExpansionModule_class(None, Sequence([MonoidPowerSeries(mpsm, {1 : m([1,2,3]), 2 : m([3,-3,2])}, mpsm.monoid().filter(4)), MonoidPowerSeries(mpsm, {1 : m([2,-1,-1]), 2 : m([1,0,0])}, mpsm.monoid().filter(4))]), PolynomialRing(ZZ, ['a', 'b']).ideal(0), DegreeGrading((1,2)))
            sage: ger._graded_monoms(2)
            [Graded expansion vector (0, 1)]
            sage: ger._graded_monoms(4)
            []
            sage: ger = GradedExpansionModule_class(Sequence([MonoidPowerSeries(mps, {1 : 2}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mpsm, {1 : m([1,2,3]), 2 : m([3,-3,2])}, mpsm.monoid().filter(4)), MonoidPowerSeries(mpsm, {1 : m([2,-1,-1]), 2 : m([1,0,0])}, mpsm.monoid().filter(4))]), PolynomialRing(ZZ, ['a', 'b', 'c']).ideal(0), DegreeGrading((1,2,3)))
            sage: ger._graded_monoms(3)
            [Graded expansion vector (a, 0), Graded expansion vector (0, 1)]
        """
        module_gens = self.grading().basis(index)
        module_gens = filter(lambda e: sum(e[self.nbasegens():]) == 1, module_gens)
        module_gens = [ filter( lambda e: e != 1,
                                [ mon**ex if ex > 1 else (mon if ex == 1 else 1) 
                                  for mon,ex in zip(self.basegens(), g[:self.nbasegens()]) + zip(self.gens(), g[self.nbasegens():]) ] )
                        for g in module_gens ]
        
        return [ reduce(operator.mul, g) if len(g) > 1 else g[0] for g in module_gens ]

    def _coerce_map_from_(self, other) :
        """
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import *
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_ring import *
            sage: m = FreeModule(QQ, 3)
            sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False))
            sage: mps = mpsm.base_ring()
            sage: ger = GradedExpansionModule_class(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mpsm, {1: m([1,1,1]), 2: m([1,3,-3])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2)))
            sage: ger._coerce_map_from_(ZZ)
        """
        if other is self.relations().ring() :
            from sage.structure.coerce_maps import CallableConvertMap
            
            return CallableConvertMap(other, self, self._element_constructor_)
        
        if isinstance(other, GradedExpansionSubmodule_abstract) :
            if other.graded_ambient() is self \
              or self.has_coerce_map_from(other.graded_ambient()) :
                from sage.structure.coerce_maps import CallableConvertMap

                return CallableConvertMap(other, self, other._graded_expansion_submodule_to_graded_ambient_)

        return Module._coerce_map_from_(self, other)

    def _element_constructor_(self, x) :
        """
        INPUT:
            - `x` -- A zero integer, an element of the underlying polynomial ring or
                     an element in a submodule of graded expansions.
        
        OUTPUT:
            An instance of the element class.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import *
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_ring import *
            sage: m = FreeModule(QQ, 3)
            sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False))
            sage: mps = mpsm.base_ring()
            sage: ger = GradedExpansionModule_class(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mpsm, {1: m([1,1,1]), 2: m([1,3,-3])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2)))
            sage: h = ger(0)
        """
        if isinstance(x, (int, Integer)) and x == 0 :
            return self._element_class(self, self.relations().ring().zero())
        
        P = x.parent()
        if P is self.base_ring() :
            if x == 0 :
                return self._element_class(self, self.relations().ring().zero())
        
        return GradedExpansionAmbient_abstract._element_constructor_(self, x)

    def _repr_(self) :
        """
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import *
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_module import *
            sage: m = FreeModule(QQ, 3)
            sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False))
            sage: mps = mpsm.base_ring()
            sage: ger = GradedExpansionModule_class(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mpsm, {1: m([1,1,1]), 2: m([1,3,-3])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2)))
            sage: ger
            Graded expansion module with generators b
        """
        return "Graded expansion module with generators " \
                + ''.join(map(lambda e: repr(e.polynomial()) + ", ", self.gens()))[:-2]
                
    def _latex_(self) :
        """
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import *
            sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import *
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_module import *
            sage: m = FreeModule(QQ, 3)
            sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False))
            sage: mps = mpsm.base_ring()
            sage: ger = GradedExpansionModule_class(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mpsm, {1: m([1,1,1]), 2: m([1,3,-3])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2)))
            sage: latex(ger)
            Graded expansion module with generators b
        """
        return "Graded expansion module with generators " \
                + ''.join(map(lambda e: latex(e.polynomial()) + ", ", self.gens()))[:-2]
